package com.gjzhao.fmmall.interceptor;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gjzhao.fmmall.constant.Constants;
import com.gjzhao.fmmall.entity.Users;
import com.gjzhao.fmmall.vo.RespResultVO;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

/**
 * @Description: token拦截器[针对受限资源进行拦截]
 * 业务流程分析：
 *  - 1.用户在请求头中携带token信息，访问受限资源
 *  - 2.拦截器拦截到请求，从redis中获取用户信息(使用redis存储token后，不用去解析token的正确性，只要能从redis获取到用户信息，就说明当前token是正确的)
 * @Author: gjzhao
 * @Date Created in 2021-12-31 10:17 下午
 */
@Component
public class CheckTokenInterceptor implements HandlerInterceptor {


    private Logger logger = LoggerFactory.getLogger(CheckTokenInterceptor.class);

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * @param request  请求
     * @param response 响应
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String method = request.getMethod();

        //放行options请求：跨域访问中，一次ajax总共会发两次请求：试探性的请求 + 真正的请求
        if (Constants.REQUEST_OPTION.equals(method)) {
            return true;
        }

        String token = request.getHeader(Constants.REQUEST_HEADER_PARAM_TOKEN);
        if (StrUtil.isBlank(token)) {
            logger.info("----当前请求未携带token参数----");
            RespResultVO resultVO = RespResultVO.failed("请携带token参数");
            doResponse(response, resultVO);

        } else {
            String userStr = stringRedisTemplate.boundValueOps(token).get();
            if (StrUtil.isBlank(userStr)) {
                // 如果当前token未匹配到用户信息，则提示前端请求的token无效(token不正确或token过期)
                logger.info("----token:{}无效----",token);
                RespResultVO resultVO = RespResultVO.failed("token无效");
                doResponse(response, resultVO);
            } else {
                //只要当前token存放的用户信息不为空，就重新设置过期时间
                Users user = JSONUtil.toBean(userStr, Users.class);
                stringRedisTemplate.boundValueOps(token).expire(30, TimeUnit.MINUTES);
                logger.info("----当前登录用户:{}，正在访问资源，重新设置token过期时间----", user.getUsername());
                return true;

            }
        }
        return false;
    }

    /**
     * 封装拦截器响应的数据
     *
     * @param response 响应
     * @param resultVO 数据
     */
    private void doResponse(HttpServletResponse response, RespResultVO resultVO) throws IOException {

        //设置响应类型/编码方式
        response.setContentType(Constants.RESPONSE_TYPE);
        response.setCharacterEncoding(Constants.CHARACTER_ENCODING_UTF8);

        //打印流
        PrintWriter writer = response.getWriter();
        String stringResult = new ObjectMapper().writeValueAsString(resultVO);
        writer.print(stringResult);
        writer.flush();
        writer.close();

    }
}
